Flash Player 11で追加されたJSONクラスのパフォーマンスと機能
JSONのネイティブサポート
Flash Player 11でJSONがサポートされたので、試してみました。
// シリアライズ JSON.stringify(obj); // デシリアライズ JSON.parse(json);
使い方は簡単ですね。
実行速度
今までFlashでJSONを使用する場合は、as3corelibなどを利用されていたかと思います。今回追加されたAPIとas3corelibの実行速度が気になりましたので、実際に比べてみました。
デシリアライズ | シリアライズ | |
---|---|---|
ネイティブAPI | 16ms | 2ms |
as3corelib | 62ms | 45ms |
デシリアライズ | シリアライズ | |
---|---|---|
ネイティブAPI | 22ms | 11ms |
as3corelib | 433ms | 90ms |
今回追加されたAPIの方が断然早いですね。as3corelibがas3で実装されているのに対し、今回追加されたJSONはFlash PlayerネイティブのAPIとして実装されているので差が顕著に出ています。大量のデータを処理する場合やモバイルの環境で利用する場合などで、フレームレートへの影響を少なくすることができそうです。
出力データの加工
JSON.parseとJSON.stringifyは第二引数に関数をとり、フィルタのような処理をすることができます。stringifyメソッドで試してみます。
var data:Array = [ { name : "ひこにゃん", age : 5, transientData : "1"}, { name : "はばタン", age : 8, transientData : "2"}, { name : "せんとくん", age : 3, transientData : "3"} ]; var jsonData:String = JSON.stringify(data, function(key:String, value:*):* { if (key == "name") { return value + "!"; } if (key == "transientData") { return undefined; } return value; } ); trace(jsonData);
実行結果です。
キーがnameのプロパティの値には末尾に「!」が加えられていることが確認できます。また、キーがtransientDataのプロパティはシリアライズされていません。関数内でundefinedを返すと、そのプロパティはシリアライズの対象にならないためです。
parseメソッドの第二引数を指定する場合も、stringifyメソッドの第二引数と同じシグネチャの関数を利用します。しかし、stringifyメソッドの引数名がreplacerとなっているのに対し、parseメソッドはreviverとなっていることからも分かるように、利用目的が異なるようです。parseメソッドの第二引数の使い方は後程紹介します。
なお、stringifyメソッドは第三引数に数値を指定すると、指定した数値の分だけスペースでインデントをつけたJSONデータを返してくれます。
先ほどのコードのstringify利用箇所で第三引数を指定してみます。
var jsonData:String = JSON.stringify(data, function(key:String, value:*):* { if (key == "name") { return value + "!"; } if (key == "transientData") { return undefined; } return value; }, 4 );
実行結果です。
オブジェクトの復元
JSONにシリアライズしたデータをオブジェクトにデシリアライズする際にparseメソッドの第二引数を利用します。まずは、parseメソッドをそのまま使用してJSONをデシリアライズしてみます。
var customer:Customer = new Customer(); customer.name = "ひこにゃん"; customer.age = 5; customer.createDate = new Date(); var json:String = JSON.stringify(customer); var parsedData:Object = JSON.parse(json); trace(ObjectUtil.toString(parsedData));
public class Customer { public var name:String; public var age:int; public var createDate:Date; }
実行結果です。
Customer型のデータがObject型で復元されてしまいました。また、Date型のcreateDateもStringになってしまっています。
そこで、parseメソッドの第二引数を利用します。デシリアライズのサンプルコードの6行目を下記のコードに置き換えて実行します。
var parsedData:Customer = JSON.parse(json, function(key:String, value:*):* { if (key == "createDate") { var date:Date = new Date(value); return date; } else if (key == "") { var customer:Customer = new Customer(); for (var key:String in value) { customer[key] = value[key]; } return customer; } return value; } ) as Customer;
実行結果です。
parseメソッドの第二引数に上記のような引数を持つ関数(以下reviver関数)を渡すと、パース処理時に連想配列のキーと値がreviver関数に渡されます。この関数内で返したオブジェクトがそのままプロパティの値として復元時にセットされます。
この例ではキーがcreateDateの値はDate型のオブジェクトを、キーが空文字の値はCustomer型のオブジェクトを、その他はそのままの値を返しています。キーが空文字の場合の値は、シリアライズしたデータそのものが渡されます。その結果、オブジェクトはしっかりとCustomer型で復元され、createDateもDate型に戻っています。
なお、パース処理は子ノードから順番に行われているようで、空文字のキーがreviver関数に渡される時には既にcreateDateの処理は終わっており、value引数で渡されるオブジェクトのcreateDateにはしっかりDate型のオブジェクトがセットされています。
シリアライズのカスタマイズ
先ほど出力データの加工のサンプルでシリアライズした際には、Date型がtoStringメソッドで返却される値でシリアライズされていました。では、stringifyが呼び出された際に内部的にtoStringが直接呼び出されたのかというと、実態は少々違うようです。
Flash Player 11でJSONが実装されたのと同時に、DateにtoJSONというメソッドが追加されました。JSONのstringifyメソッドが呼び出された際に、シリアライズ対象のオブジェクトがtoJSONという名前のメソッドを持っているかチェックし、持っていればそのメソッドで返される値を使ってシリアライズするという仕組みになっており、それに対応するためです。DateはtoJSONの中でtoStringの値を返す実装になっているので、結果的にtoStringで返される値でシリアライズされた形になりました。
このことから、DateクラスのようにtoJSONを実装することによって、シリアライズのフォーマットをカスタマイズすることができそうだということが分かります。また、自クラスのコンストラクタなどでパースできる文字列を返すよう実装すれば、parseメソッドのreviver関数と組み合わせることでデシリアライズを容易にすることができそうです。
なお、toJSONはDateクラスの他に、ByteArray、Dictionary、XMLクラスでも実装されています。
まとめ
今回追加されたJSONの実装は、高速化のみならず様々な機能が実装されています。これらをうまく活用できれば、JSONまわりのソースコードをより簡潔に書くことができるかもしれません。
参考サイト
JSON - ActionScript 3.0 Reference for the Adobe Flash Platform
Working with Native JSON in Flash Player 11